package com.theme.common.third.apple;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.theme.common.base.JSONHandler;
import com.theme.common.base.http.HttpHandler;
import com.theme.common.third.apple.bean.CusJws;
import io.jsonwebtoken.*;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @name: https://zhuanlan.zhihu.com/p/140308512 <tb>
 * @title: sub对应微信的unionid还是openid？
 *         unionid 一个开发者下的的应用，sub都是一样的<tb>
 * @author: cuixinfu@51play.com <tb>
 * @date: 2021/10/25 9:08:04<tb>
 */
public class AppleLoginHandler1 {
    private static Logger logger = LoggerFactory.getLogger(AppleLoginHandler1.class);

    private static String appleIssuerUrl = "https://appleid.apple.com";
    private static String appleAuthUrl = "https://appleid.apple.com/auth/keys";
    private static String appleAud = "com.lyy.test";

    private static Map<String, Map<String, String>> map;
    private static String kty;

    public AppleLoginHandler1() {
        try {
            // https://developer.apple.com/documentation/sign_in_with_apple/jwkset/keys
            String str = HttpHandler.doGetStr2(appleAuthUrl);
            JSONObject data = JSONObject.parseObject(str);
            System.err.println("keys=====>" + JSONObject.toJSONString(data));
            JSONArray jsonArray = data.getJSONArray("keys");
            map = new HashMap<String, Map<String, String>>();
            for (int i = 0; i < jsonArray.size(); i++) {
                kty = jsonArray.getJSONObject(i).getString("kty");

                Map<String, String> m = new HashMap<String, String>();
                m.put("n", jsonArray.getJSONObject(i).getString("n"));
                m.put("e", jsonArray.getJSONObject(i).getString("e"));
                map.put(jsonArray.getJSONObject(i).getString("kid"), m);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    static {
        try {
            // https://developer.apple.com/documentation/sign_in_with_apple/jwkset/keys
            String str = HttpHandler.doGetStr2(appleAuthUrl);
            JSONObject data = JSONObject.parseObject(str);
            System.err.println("keys=====>" + JSONObject.toJSONString(data));
            JSONArray jsonArray = data.getJSONArray("keys");
            map = new HashMap<String, Map<String, String>>();
            for (int i = 0; i < jsonArray.size(); i++) {
                kty = jsonArray.getJSONObject(i).getString("kty");

                Map<String, String> m = new HashMap<String, String>();
                m.put("n", jsonArray.getJSONObject(i).getString("n"));
                m.put("e", jsonArray.getJSONObject(i).getString("e"));
                map.put(jsonArray.getJSONObject(i).getString("kid"), m);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 对前端传来的JWT字符串identityToken的第二部分进行解码
     * 主要获取其中的aud和sub，aud对应ios前端的包名，sub对应当前用户的授权openID
     *
     * @param identityToken
     * @return
     */
    public static Map<String, JSONObject> parserIdentityToken(String identityToken) {
        Map<String, JSONObject> map = new HashMap<String, JSONObject>();

        String[] arr = identityToken.split("\\.");

        String deHeader = new String(Base64.decodeBase64(arr[0]));
        logger.info("header=" + deHeader);
        JSONObject header = JSON.parseObject(deHeader);
        map.put("header", header);

        String dePayload = new String(Base64.decodeBase64(arr[1]));
        logger.error("dePayload="+dePayload);
        JSONObject payload = JSON.parseObject(dePayload);
        map.put("payload", payload);
        return map;
    }

    /**
     * 对前端传来的identityToken进行验证
     *
     * @param identityToken
     * @return openId
     */
    public static AppleRpcResult verifyToken(String identityToken) {
        logger.info("appleIssuerUrl:{}, appleAuthUrl:{}, appleAud:{}" + appleIssuerUrl + appleAuthUrl + appleAud);

        try {
            // 1 解析
            Map<String, JSONObject> json = parserIdentityToken(identityToken);
            JSONObject header = json.get("header");
            logger.error("token.header:{}",JSONObject.toJSONString(header));
            String kid = json.get("header").getString("kid");
            // 2 生成publicKey
            PublicKey publicKey = getPublicKey(kid);
            if (publicKey == null) {
                logger.error("failed to get publicKey");
                return AppleRpcResult.error("获取苹果验证公钥失败");
            }
            // 3 验证  https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
            JwtParser jwtPaser = Jwts.parserBuilder()
                    .requireIssuer(appleIssuerUrl)
                    .requireAudience(appleAud)
                    .setSigningKey(publicKey).build();

            Jws<Claims> claim = jwtPaser.parseClaimsJws(identityToken);
            if (claim != null) {
                System.err.println("claim:" + claim);
                String sub = claim.getBody().get("sub").toString();//sub,即用户的Apple的openId
                String iss = claim.getBody().get("iss").toString();
                String aud = claim.getBody().get("aud").toString();
                Long exp = Long.valueOf(claim.getBody().get("exp").toString())*1000;//exp is second
                if (appleIssuerUrl.equals(iss)
                        && appleAud.equals(aud)
                        && System.currentTimeMillis() < exp) {
                    return AppleRpcResult.ok(sub);
                }
            }
            return AppleRpcResult.error("苹果登陆授权 idToken 验证失败");
        } catch (ExpiredJwtException e) {
            System.err.println("Apple idToken expired:{}" + e);
            return AppleRpcResult.error("苹果登陆授权 idToken 已过期");
        } catch (Exception e) {
            System.err.println("Apple idToken illegal:{}" + e);
            return AppleRpcResult.error("非法的苹果登陆授权 idToken");
        }
    }

    /**
     * 获取苹果的公钥
     * @param kid 密钥类型
     * @return
     *
     * @throws Exception
     */
    public static PublicKey getPublicKey(String kid) {
        logger.info("kid:{}",kid);
        try {
            String n = map.get(kid).get("n");
            String e = map.get(kid).get("e");
            logger.info("n={}" , n);
            logger.info("e={}" , e);

            BigInteger modulus = new BigInteger(1, Base64.decodeBase64(n));
            BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(e));

            RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);
            KeyFactory kf = KeyFactory.getInstance(kty);//目前kty均为 "RSA"
            return kf.generatePublic(spec);
        } catch (Exception e) {
            System.err.println("getPublicKey error:{}" + e);
        }
        return null;
    }

    public static void main(String[] args) throws Exception {
        // 前端传来的 identityToken（base64编码的JWT）
        // String jwt =
        // "eyJraWQiOiI4NkQ4OEtmIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmNoYW5nZGFvLnR0c2Nob29sIiwiZXhwIjoxNTg5MTg1Mjg1LCJpYXQiOjE1ODkxODQ2ODUsInN1YiI6IjAwMTk0MC43YTExNDFhYTAwMWM0NjllYTE1NjNjNmJhZTk5YzM3ZC4wMzA3IiwiY19oYXNoIjoiN1gzc2x2dHVBU0kwYmFSbU0wVGFrQSIsImVtYWlsIjoiYXEzMmsydnpjd0Bwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTU4OTE4NDY4NSwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.S9wCOt6EeOoRrSMq4kUkPgJPyP1ruMXEcEZeeQEd1CDpcyVWLI8nTOqrl-l0sWYR-5nl2-1iJyiu77fRv8T7dBoV0EHT7GgM1l7qhnWsI9I8V-56rA9ArdJrLIBJbxu7j-xzQhZb6PZ5MSxPZ6WqZay0RpP9JiQ23ybssWQsMnqzvVZkye0iNtBGT1LnfT80XNxmj8L2uJZY08mXjjWWsYY_h0_IRvqOLyaW99w-F8T9KuDkWz2Z-DJX_tiKC0DOT03ypBv82H0v_v-8lFlp4rNRSB82CdgfYwEWElU7zKZfaHJOxT3wOvRXNpbj6_hENPdbtG2ozgdg2oVEiamz0g";
        String jwt =
                "eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmNoYW5nZGFvLnR0c2Nob29sIiwiZXhwIjoxNTg5MjcwMzI3LCJpYXQiOjE1ODkyNjk3MjcsInN1YiI6IjAwMTk0MC43YTExNDFhYTAwMWM0NjllYTE1NjNjNmJhZTk5YzM3ZC4wMzA3IiwiY19oYXNoIjoienNIUW9xbTdjcDZOcmxrUHFhTmpGQSIsImVtYWlsIjoiYXEzMmsydnpjd0Bwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTU4OTI2OTcyNywibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.q5unOzswOjpRYmrVKVm3FRb_Th6kkhgEvoFfTEAIETwgTXZ7bYcQM8J8tCjkGGqtt2z74Z-wTW7Q3ia209xhmwrVDIup0jcQgNTvsCEMkfo9evPIDrNRNQw2Dzw2EBKma8004NL6THYlySoDnPRoW_VQCHP_m0HnjYuIc-wtREEClf-_tOFDPpTsvUFoETHNfhpsLhqj24-zm6MSOocYY3WbUaYJQVEFCz-x6AGko1XkMtms_-JU1xakNtjMZTIVj2XyUI5MO7_eo-D9i_c7Hj-OE9HNBEvFnPxOesDzXvEoYdb7uByXEfa-H1syJMecBMRa3tL76W_CYKsONRkU9Q";
        // String jwt =
        // "eyJraWQiOiJBSURPUEsxIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLnNreW1pbmcuYXBwbGVsb2dpbmRlbW8iLCJleHAiOjE1NjU2NjU1OTQsImlhdCI6MTU2NTY2NDk5NCwic3ViIjoiMDAwMjY2LmRiZTg2NWIwYWE3MjRlMWM4ODM5MDIwOWI5YzdkNjk1LjAyNTYiLCJhdF9oYXNoIjoiR0ZmODhlX1ptc0pqQ2VkZzJXem85ZyIsImF1dGhfdGltZSI6MTU2NTY2NDk2M30";
        // String jwt =
        // "eyJraWQiOiJlWGF1bm1MIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmNoYW5nZGFvLnR0c2Nob29sIiwiZXhwIjoxNTg5MjgzMTc4LCJpYXQiOjE1ODkyODI1NzgsInN1YiI6IjAwMTk0MC43YTExNDFhYTAwMWM0NjllYTE1NjNjNmJhZTk5YzM3ZC4wMzA3IiwiY19oYXNoIjoiV1pLajM1LW44SDVDejBPWVRsWjFXQSIsImVtYWlsIjoiYXEzMmsydnpjd0Bwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTU4OTI4MjU3OCwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.ZXTkeWDnVqJu2F3PPTYFBGEqYeRS7w2Xi2f1k7IBR0sLIlmcYRG2Wm5qIiXn1YXmWOXPc7B6fBHl4KdwG5oRewN0BplLQvuwrbonsIfbSG4GQKxxUdPvGObKLx19N9z8EKCeqNifrVvDXlHLZEDvT04t6U5tz-QWDYOce-yGFd8HjnG4hnKPu98pIwzQMTbCt-c3FTnAID42NBxwaY71DUth3JT5sk7WPY_WRcr0Q3WqX2WO7z_UZnIOWnSCNoLbHVboBPuomwZ_P3klnoQWFbNGHLlVJLroz89W7lMr_c_cxc_cbHexhdGykcxgRWsyW2GbZFdPBQVvbGmvw3beug";

        //String jwt = "eyJraWQiOiI4NkQ4OEtmIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmNoYW5nZGFvLnR0c2Nob29sIiwiZXhwIjoxNTg5Mjg2ODcyLCJpYXQiOjE1ODkyODYyNzIsInN1YiI6IjAwMTk0MC43YTExNDFhYTAwMWM0NjllYTE1NjNjNmJhZTk5YzM3ZC4wMzA3IiwiY19oYXNoIjoiX0ZUSlh3cGhpRFhHeEhIQ1VMODdVZyIsImVtYWlsIjoiYXEzMmsydnpjd0Bwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJpc19wcml2YXRlX2VtYWlsIjoidHJ1ZSIsImF1dGhfdGltZSI6MTU4OTI4NjI3Miwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.c2h4MoEjGWRmJAAppbvuJToTGf8wM510yQYgonZXIwan66KTAmYLE11NpA83xq0pvtuex-hbRjna4WeJWD1QfZsHlZ7iIhL95YoG9y8DXIhTyrd51ADLxn4gEyxOKM03aZ4M54NyoZ13V3osd-T-1tvCX86JZnDlrWkixsUONiXLXB9-G63QO5JwsQPJuorweT9-qj6NIiZmX_ayDhRFpe0FxW41u-c3LhN4dRTb1FMF2LYDymBYsdLNyAv7glDzq13M6rBeQRMRJz7e5C6PcppHhhxgrbJTcdCszB5bjA-Ck8PbnsM2qSxVW6hHd4xEpClEzUMFee8dZIi1PSU0KA";
        //校验基本信息:nonce,iss,aud,exp
        CusJws cusJws = CusJws.getJws(jwt);
        String cusJwsStr = JSONHandler.getGson().toJson(cusJws);
        logger.info("获取基本信息,解析identityToken结果：{}",cusJwsStr);
        logger.info("parser: " + AppleLoginHandler1.parserIdentityToken(jwt));
        logger.info("verify: " + AppleLoginHandler1.verifyToken(jwt));
    }

}